// XIM服务组件，用于提供xim的功能性接口
package xginxim

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"

	"gitee.com/xiaoyutab/xgotool/optional/xim"
	"gitee.com/xiaoyutab/xgotool/optional/xnsq"
	"gitee.com/xiaoyutab/xgotool/xgin"
	"gitee.com/xiaoyutab/xgotool/xstring"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

// 此方法中存在以下快捷方法，用于快速提取请求中的变量信息
// c.GetUint("uid") 获取当前用户ID，大于0
// c.GetUint("dervice_id") 获取当前IM的设备ID，可能为0

// ws协议中的请求参数结构
type wsMessage struct {
	Key   string `json:"key" form:"key"`     // 请求的方法
	Value any    `json:"value" form:"value"` // 携带的参数
}

var _conn map[uint][]*websocket.Conn = map[uint][]*websocket.Conn{}
var _conn_lock sync.RWMutex

// 注入IM相关的路由和基础配置
// 此组件中并未有获取个人信息的接口，请在外部手动封装根据uid获取用户详情信息的接口
//
//	r	分组路由
//	c	XIM配置项信息，如果提前注入了此处可以传nil
//
// PS: 此服务使用了 c.GetUint("uid")>0 的鉴权，所以注入时请先在其他中间件中将uid设置到c变量中
func Regedit(r *gin.RouterGroup, c *xim.Config) func(c *gin.Context) {
	// 注入IM配置
	xim.Regedit(c)
	// 注入依赖配置项
	xim.Regedit(&xim.Config{
		NsqMessageSend: "xginxim.nsq.send.message",
	})
	// 追加中间件
	r.Use(xgin.Page, xgin.MustLogin)
	// 定义升级方式
	var upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	var rout map[string]func(uint, []byte) *xgin.Response = map[string]func(uint, []byte) *xgin.Response{
		"reg.dervice":       regDervice,      // 注册用户设备
		"der.heartbeat":     derHeartbeat,    // 设备心跳维持
		"user.lately.chat":  userLatelyChat,  // 最近聊天窗列表
		"user.chat.users":   userChatUsers,   // 聊天群成员列表
		"user.chat.send":    userChatSend,    // 发送消息
		"user.chat.message": userChatMessage, // 历史消息记录
	}
	xnsq.AddNsqConf("xginxim.nsq.send.message", &xnsq.NsqFuncConf{
		Name: "发送消息后的推送列表",
		Desc: "IM消息发送后，使用此队列给各连接的websocket客户端推送消息记录",
		Func: nsq_send_message,
	})
	return func(c *gin.Context) {
		// 升级HTTP连接为WebSocket连接
		conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
		if err != nil {
			c.JSON(400, xgin.Resp().ErrorString("Failed to upgrade WebSocket connection: "+err.Error()).GetData())
			return
		}
		defer conn.Close()
		addConn(c.GetUint("uid"), conn)
		for {
			// 读取客户端发送的消息
			_, msg, err := conn.ReadMessage()
			if err != nil {
				fmt.Println("消息读取失败：", err)
				removeConn(c.GetUint("uid"), conn)
				// 连接手动断开/意外断开
				break
			}
			if len(msg) <= 0 {
				time.Sleep(time.Millisecond * 200) // 休眠200毫秒
				continue
			}
			t := wsMessage{}
			if err := json.Unmarshal(msg, &t); err != nil {
				conn.WriteJSON(xgin.Resp().ErrorString("Failed to read json: " + err.Error()).GetData())
				continue
			}
			// 获取相应结果，并将之返回出去
			if v, ok := rout[t.Key]; ok {
				// 转化请求参数
				b, _ := json.Marshal(t.Value)
				// 获取处理结果
				inf := v(c.GetUint("uid"), b).GetData()
				// 往inf中注入key类型
				if m, ok := inf.(gin.H); ok {
					m["key"] = t.Key
					inf = m
				}
				// 将处理结果回写到客户端
				conn.WriteJSON(inf)
			} else {
				conn.WriteJSON(xgin.Resp().ErrorString("Key not found: " + t.Key).GetData())
			}
		}
	}
}

// 添加读写锁
//
//	uid		用户ID
//	conn	当前连接
func addConn(uid uint, conn *websocket.Conn) {
	_conn_lock.Lock()
	defer _conn_lock.Unlock()
	_conn[uid] = append(_conn[uid], conn)
}

// 移除读写锁
//
//	uid		用户ID
//	conn	待移除的读写锁
func removeConn(uid uint, conn *websocket.Conn) {
	_conn_lock.Lock()
	defer _conn_lock.Unlock()
	for i := 0; i < len(_conn[uid]); i++ {
		if _conn[uid][i] == conn {
			// 删除该元素
			_conn[uid] = append(_conn[uid][:i], _conn[uid][i+1:]...)
		}
	}
	_conn[uid] = append(_conn[uid], conn)
}

// 发送IM消息服务
//
//	uid	用户ID
//	inf	发送内容
func XSendWebsocketMessage(uid uint, inf *xgin.Response) {
	sendMessage(uid, inf)
}

// 发送IM消息记录
//
//	uid	用户ID
//	inf	发送内容
func sendMessage(uid uint, inf *xgin.Response) {
	_conn_lock.RLock()
	defer _conn_lock.RUnlock()
	mess := inf.GetData()
	if m, ok := mess.(gin.H); ok {
		m["key"] = "im.push"
		mess = m
	}
	for i := 0; i < len(_conn[uid]); i++ {
		_conn[uid][i].WriteJSON(mess)
	}
}

// 分页结构体
type Pages struct {
	InputPage  json.Number `json:"page" form:"page"`
	InputLimit json.Number `json:"limit" form:"limit"`
}

// 获取条数
func (p *Pages) Offset() int {
	return xgin.GetOffset(xstring.JInt(p.InputPage), xstring.JInt(p.InputLimit))
}

// limit条数(默认10)
func (p *Pages) Limit() int {
	limits := xstring.JInt(p.InputLimit)
	if limits <= 0 {
		return 10
	}
	return limits
}

// page页码
func (p *Pages) Page() int {
	pages := xstring.JInt(p.InputPage)
	if pages <= 0 {
		return 1
	}
	return pages
}
